//=================================================================================//
// database definition for CEM simulation
// Author : Wang Yong
// Date   : 2020.10.28
//=================================================================================//
#ifndef DATA_BASE_H
#define DATA_BASE_H

#include <QString>
#include <string>
#include <vector>
#include "Node.h"
#include "./ThirdParty/tinyxml2/tinyxml2.h"

constexpr int SYMM_TYPE[5] = { 0,1,-1,2,-2 };

enum CEM_ALG {
    ALG_MoM = 0,
    ALG_MLFMA=1,
    ALG_FEM = 2,
    ALG_UTD = 3,
    ALG_PO  = 4
};

struct FreqPara {
    double dFreq0{ 0 };
    double dMinFreq{ 0 };
    double dMaxFreq{ 0 };
    unsigned iNumFreq{ 0 };
    std::string sFreqUnit{ "GHz" };
    std::string sSwpType{ "Interpolate" };
};

struct SolvPara {
    int iAlgType{ ALG_MoM };
    std::string sAccuracy{ "Normal" };

    // parameter for MoM
    bool bHobMoM{ false };

    // parameter for MLFMA
    int iFmmIterNum{ 500 };
    std::string sFmmItrType;
    std::string sFmmPreCond;
    double dFmmIterErr{ 0.005 };
    double dFmmBoxSize{ 0.4 };
    double dFmmIERatio{ 0.8 };

    // parameter for FEM
    bool bHobFEM{ false };
    bool bMetalIn{ false };
    bool bEigMode{ false };
    bool bAdaptMsh{ true };
    std::string sBoundary{ "PEC" };
    std::string sFemSolver;         // Direct, Iteration, DDM
    int iFemMaxIterNum{ 20 };
    double dFemIterErr{ 0.01 };
    // parameter for eigen
    int iEigModeNum{ 1 };
    double dEigMinFreq{ 0 };
    // adaptive mesh parameter
    double dAdaptRatio{ 20 };
    double dAdaptDelta{ 0.02 };
    int iAdapMinNum{ 2 };
    int iAdapMaxNum{ 10 };
    int iAdaPassNum{ 1 };
};

struct Material {
    std::string sName;
    int iID{ 0 };
    bool bTensor{ false };
    double dEpsr{ 1 };
    double dTanE{ 0 };
    double dMiur{ 1 };
    double dTanM{ 0 };
    double dSgma{ 0 };

};

struct PreFarsSet {
    std::string sName;
    int iNumPh{ 0 };
    int iNumTh{ 0 };
    double dMinPh{ 0 };
    double dMaxPh{ 0 };
    double dMinTh{ 0 };
    double dMaxTh{ 0 };
};

struct PreNearSet {
    std::string sName;
    std::string sFile;
    bool bUserType{true};
    double dMinX{ 0 };
    double dMinY{ 0 };
    double dMinZ{ 0 };
    double dMaxX{ 0 };
    double dMaxY{ 0 };
    double dMaxZ{ 0 };
    double dX{ 0 };
    double dY{ 0 };
    double dZ{ 0 };
    // for mesh file
    std::vector<Node> vR;
    std::vector<std::vector<int>> vvTria;
    std::vector<std::vector<int>> vvQuad;
};

struct SurfMesh {
    std::vector<Node> vR;
    std::vector<std::vector<int>> vvBoud;
    std::vector<std::vector<int>> vvWire;
    std::vector<std::vector<int>> vvTria;
    std::vector<std::vector<int>> vvQuad;
    std::vector<double> vRadius;
};

struct BoudCond {
    std::string sType;
    int iID;
    int iTriaNum;
    std::vector<std::vector<int>> vvTria;
};

struct VoluMesh {
    std::vector<Node> vR;
    std::vector<std::vector<int>> vvTetr;
    std::vector<BoudCond> vBoud;
};

struct PlanWave {
    std::string sName;
    int iNumPh{ 1 };
    int iNumTh{ 1 };
    int iRot{ 0 };
    double dMinPh{ 0 };
    double dMaxPh{ 0 };
    double dMinTh{ 0 };
    double dMaxTh{ 0 };
    double dPolar{ 0 };
    double dOAR{ 0 };
    std::vector<double> vMag;
    std::vector<double> vPha;
};

struct WirePort {
    std::string sName;
    Node Oc;
    Node ei;
    double z0{ 50 };
    std::vector<double> vMag;
    std::vector<double> vPha;
};

struct LumpPort {
    std::string sName;
    Node Oc;
    Node ei;
    Node norm;
    double dWidth{ 0 };
    double dHeigh{ 0 };
    double z0{ 50 };
    std::vector<double> vMag;
    std::vector<double> vPha;
};

struct WavePort {
    std::string sName;
    std::string sType;
    std::string sMode{"TE"};
    Node Oc;
    Node ai;
    Node ei;
    double a{ 0 };
    double b{ 0 };
    double z0{ 50 };
    int iBoundID;
    int m{1};
    int n{0};
    std::vector<double> vMag;
    std::vector<double> vPha;
};

struct EquivSrc {
    std::string sName;
    std::string sFile;
    std::string sFldFileE;
    std::string sFldFileH;
    int iType;  // 0:equ,  1:fld,  2:near
    bool bSaved{false};
    Node Oc;
    double theta;
    double phi;
    double alpha;

    std::vector<double> vMag;
    std::vector<double> vPha;
};

class DataBase
{
public:
    DataBase();
    ~DataBase() {}

    FreqPara m_freqSet;
    SolvPara m_solvSet;
    int m_pSymSet[3]{ 0 };
    int m_iNumVolt{ 1 };
    int m_iNumCPU{ 1 };
    bool m_bCalcCurr{ false };
    std::string m_sMeshName;
    std::string m_sMpiExec;

    SurfMesh m_surf;
    VoluMesh m_volu;

    std::vector<Material> m_vMedi;
    std::vector<PlanWave> m_vWave;
    std::vector<WirePort> m_vWGap;
    std::vector<LumpPort> m_vLump;
    std::vector<WavePort> m_vPort;
    std::vector<EquivSrc> m_vEqui;
    std::vector<PreFarsSet> m_vFars;
    std::vector<PreNearSet> m_vNear;

    bool fn_LoadFile(const std::string &sName);
    bool fn_LoadNear(const std::string &sName);
    bool fn_WriteXML(const std::string &sName);
    bool fn_LoadMesh(const std::string &sName);
    bool fn_SaveMesh(const std::string &sName);
    void fn_SaveNear(const std::string &sPath);
    bool fn_ExportGidMsh(const std::string &sName);
    bool fn_OutNearPoint(const std::string &sName);
    void fn_UpdateRadius(double &dRad);
    int  fn_GetSourceNum();
    int  fn_GetPortNum();
    void fn_RemoveMedi(const std::string &sName);
    void fn_RemoveFars(const std::string &sName);
    void fn_RemoveNear(const std::string &sName);
    void fn_RemoveWave(const std::string &sName);
    void fn_RemoveWGap(const std::string &sName);
    void fn_RemoveLump(const std::string &sName);
    void fn_RemovePort(const std::string &sName);
    void fn_RemoveEqui(const std::string &sName);
    void fn_ClearData();
    bool fn_CheckData(QString &sLog);
    

private:
    bool fn_LoadGdm(const std::string &sName);
    bool fn_LoadTet(const std::string &sName);
    bool fn_LoadOcm(const std::string &sName);
    bool fn_SaveGdm(const std::string &sName);
    bool fn_SaveTet(const std::string &sName);
    // load the frequency parameter
    bool fn_LoadFreqPara(tinyxml2::XMLElement *pRoot);
    // load material parameter
    bool fn_LoadMediPara(tinyxml2::XMLElement *pRoot);
    // load symmetric parameter
    bool fn_LoadSymmetry(tinyxml2::XMLElement *pRoot);
    // load domain setting parameter
    bool fn_LoadDomaPara(tinyxml2::XMLElement *pRoot);
    // load post setting parameter
    bool fn_LoadPostPara(tinyxml2::XMLElement *pRoot);
    // load voltage parameter
    void fn_LoadVoltPara(tinyxml2::XMLElement *pRoot);
    // load parameter for all solver
    void fn_LoadSolvPara(tinyxml2::XMLElement *pRoot);
    // load algorithm setting parameter
    void fn_LoadAlgoPara(tinyxml2::XMLElement *pRoot);
    // load plane wave parameter
    void fn_LoadPlanWave(tinyxml2::XMLElement *pRoot);
    // load wire delta parameter
    void fn_LoadWirePort(tinyxml2::XMLElement *pRoot);
    // load lump port parameter
    void fn_LoadLumpPort(tinyxml2::XMLElement *pRoot);
    // load waveport parameter
    void fn_LoadWavePort(tinyxml2::XMLElement *pRoot);
    // load equivalent source
    bool fn_LoadEquivSrc(tinyxml2::XMLElement *pRoot);
    // set the voltage to source
    void fn_SetVoltToSrc(std::string& sName, std::vector<double>& vMag, std::vector<double>& vPha);
    // set the number of voltage group
    void fn_SetVoltGroup();
};

#endif
